﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace CompileApp.Models;

public class Nfa
{
    // 所有非终结符对应的含右式式的集合
    private Dictionary<string, List<List<string>>> _productions = new();

    public Dictionary<string, List<Edge>> Edges = new();

    public Dictionary<Tuple<string, string>, List<string>> EdgeTable = new();

    // 所有非终结符
    public HashSet<string> NonTerminators;
    public HashSet<string> Terminators = new HashSet<string>();

    private int _flag = 2;

    public string StartNode = "";
    public string EndNode = "Z";

    private string _resultText = "";

    public Nfa(string grammar)
    {
        string[] rules = grammar.Split('\n');

        foreach (var rule in rules)
        {
            // 忽略注释
            if (rule.Trim().IndexOf("//") == 0) continue;
            string[] parts = rule.Split(new string[] { "->", "|" }, StringSplitOptions.RemoveEmptyEntries);

            if (parts.Length <= 1)
            {
                continue;
            }

            if (StartNode.Equals(""))
            {
                StartNode = parts[0];
            }

            if (!_productions.ContainsKey(parts[0]) && parts.Length > 1)
            {
                _productions.Add(parts[0], new List<List<string>>());
            }

            for (int i = 1; i < parts.Length; i++)
            {
                string[] items = parts[i].Split(new string[] { "[", "]" }, StringSplitOptions.RemoveEmptyEntries);

                _productions[parts[0]].Add(Enumerable.ToList(items));
            }
        }

        NonTerminators = new HashSet<string>(_productions.Keys);

        foreach (var (key, value) in _productions)
        {
            Edges.Add(key, new List<Edge>());
            foreach (var item in value)
            {
                if (item.Count > 1)
                {
                    if (_flag == 2)
                    {
                        _flag = _productions.ContainsKey(value[0][0].ToString()) ? 0 : 1;
                    }

                    Edges[key].Add(new Edge(key, item[_flag].ToString(), item[1 - _flag].ToString()));
                    Tuple<string, string> tuple = new Tuple<string, string>(key, item[1 - _flag].ToString());
                    if (!EdgeTable.ContainsKey(tuple))
                    {
                        EdgeTable.Add(tuple, new List<string>());
                    }

                    Terminators.Add(item[1 - _flag].ToString());
                    EdgeTable[tuple].Add(item[_flag].ToString());
                }
                else
                {
                    if (NonTerminators.Contains(item[0]))
                    {
                        Edges[key].Add(new Edge(key, item[0], "$"));
                        Tuple<string, string> tuple = new Tuple<string, string>(key, "$");
                        if (!EdgeTable.ContainsKey(tuple))
                        {
                            EdgeTable.Add(tuple, new List<string>());
                        }

                        EdgeTable[tuple].Add(item[0]);
                    }
                    else
                    {
                        Edges[key].Add(new Edge(key, EndNode, item[0]));
                        Tuple<string, string> tuple = new Tuple<string, string>(key, item[0]);
                        if (!EdgeTable.ContainsKey(tuple))
                        {
                            EdgeTable.Add(tuple, new List<string>());
                        }

                        Terminators.Add(item[0]);
                        EdgeTable[tuple].Add(EndNode);
                    }
                }
            }
        }
    }

    public string GetNfaText()
    {
        if (_resultText.Equals(""))
        {
            foreach (var (key, edges) in Edges)
            {
                foreach (var edge in edges)
                {
                    _resultText = $"{_resultText}{edge.StartNode}\t{edge.EndNode}\t{edge.TranStr}\n";
                }
            }
        }

        return _resultText;
    }
}

public struct Edge
{
    public string StartNode;
    public string EndNode;
    public string TranStr;

    public Edge(string startNode, string endNode, string tranStr)
    {
        this.StartNode = startNode;
        this.EndNode = endNode;
        this.TranStr = tranStr;
    }
}